#include "include.h"
#include "bsp_spiflash1.h"
xcfg_cb_t xcfg_cb;
sys_cb_t sys_cb AT(.buf.bsp.sys_cb);
volatile int micl2gnd_flag;
volatile u32 ticks_50ms;
void sd_detect(void);
void tbox_uart_isr(void);
void testbox_init(void);
void fmrx_var_init(void);

#if EXFLASH_MUSIC_WARNING
extern volatile bool wav_output_en;
void wav_output_tick_start(void);
#endif // EXFLASH_MUSIC_WARNING

void bsp_stretch_init(void);
void dac_to_spi_enable(bool en);

void sco_huart_init(void);

#if USB_SD_UPDATE_BACKUPS
bool usbsd_bkupdate_en(void)
{
    return USB_SD_UPDATE_BACKUPS;
}
#else
void usbsd_bkupdate_offset_init(void) {};
u32 get_usbsd_bkupdate_loader_offset(void) {
    return 0;
}
#endif


#if UDISK_SECTOR_4K_EN
u8 win_sector[4][512];
void disk_sector_4k_init(u8 (*buff)[512]);
#endif // UDISK_SECTOR_4K_EN

#if SD_SOFT_DETECT_EN

AT(.text.bsp.sys.init)
void sd_soft_detect_poweron_check(void)  //开机检测
{
    if (!SD_IS_SOFT_DETECT()) {  //配置工具中是否配置sd检测.
        return;
    }
    dev_delay_times(DEV_SDCARD, 1);  //检测到1次成功, 则认为SD在线.
    u8 i = 5;
    while (i--) {
        sd_soft_cmd_detect(0);
        if (dev_is_online(DEV_SDCARD)) {
            break;
        }
        delay_ms(10);
    }
    dev_delay_times(DEV_SDCARD, 3);
}


AT(.com_text.detect)
void sd_soft_cmd_detect(u32 check_ms) //check_ms 时间间隔检测一次.  //主循环中执行检测.
{
    static u32 check_ticks = 0;
    if (!SD_IS_SOFT_DETECT()) {  //配置工具中是否配置sd检测.
        return;
    }
    if (tick_check_expire(check_ticks, check_ms) || (0 == check_ticks)) {  //每隔100ms才检测一次.
        check_ticks = tick_get();
    } else {
        return;
    }
    if (sd_soft_detect()) {
        if (dev_online_filter(DEV_SDCARD)) {
            sd_insert();
            msg_enqueue(EVT_SD_INSERT);
            //my_printf("\nsd soft insert\n");
        }
    } else {
        if (dev_offline_filter(DEV_SDCARD)) {
            sd_remove();
            msg_enqueue(EVT_SD_REMOVE);
            //my_printf("\nsd soft remove\n");
        }
    }
}
#endif // SD_SOFT_DETECT_EN


#if MUSIC_SDCARD_EN
AT(.com_text.detect)
void sd_detect(void)
{
    if ((!is_sd_support()) || (IS_DET_SD_BUSY())) {
        return;
    }
#if SD_SOFT_DETECT_EN
    if (SD_IS_SOFT_DETECT()) {
        return;
    }
#endif // SD_SOFT_DETECT_EN
    if (SD_IS_ONLINE()) {
        if (dev_online_filter(DEV_SDCARD)) {
            sd_insert();
            msg_enqueue(EVT_SD_INSERT);
            //            my_printf("sd insert\n");
        }
    } else {
        if (dev_offline_filter(DEV_SDCARD)) {
            sd_remove();
            msg_enqueue(EVT_SD_REMOVE);
            //            my_printf("sd remove\n");
        }
    }
}

//#if MUSIC_UDISK_EN
//void usbchk_switch_otg_host(void);
///* 设置 USB 检查的初始状态 */
//void usbchk_switch_init_state(void)
//{
//    usbchk_switch_otg_host();
//}
//#endif // MUSIC_UDISK_EN
#endif // MUSIC_SDCARD_EN

#if MUSIC_SDCARD1_EN
AT(.com_text.detect)
void sd1_detect(void)
{
    if ((!is_sd1_support()) || (IS_DET_SD1_BUSY())) {
        return;
    }
    if (SD1_IS_ONLINE()) {
        if (dev_online_filter(DEV_SDCARD1)) {
            sd1_insert();
            msg_enqueue(EVT_SD1_INSERT);
            //            my_printf("sd1 insert\n");
        }
    } else {
        if (dev_offline_filter(DEV_SDCARD1)) {
            sd1_remove();
            msg_enqueue(EVT_SD1_REMOVE);
            //            my_printf("sd1 remove\n");
        }
    }
}
#endif // MUSIC_SDCARD_EN

#if SD_USB_MUX_IO_EN
static u8 usb_chk_sta = 0;
void get_usb_chk_sta_convert(void)
{
    while (usb_chk_sta) {
        WDT_CLR();
    }
}
#endif

AT(.com_text.detect)
u8 get_usbtf_muxio(void)
{
#if SD_USB_MUX_IO_EN
    return 1;
#else
    return 0;
#endif
}

#if USB_SUPPORT_EN
AT(.com_text.detstr)
char usb_insert_str[] = "udisk insert\n";
AT(.com_text.detstr)
char usb_remove_str[] = "udisk remove\n";
AT(.com_text.detstr)
char pc_insert_str[] = "pc insert\n";
AT(.com_text.detstr)
char pc_remove_str[] = "pc remove\n";

AT(.com_text.detect)
void usb_detect(void)
{
    if (!is_usb_support()) {
        return;
    }

#if SD_USB_MUX_IO_EN
    if (is_det_sdcard_busy()) {
        return;
    }
#if (SD0_MAPPING == SD0MAP_G5 || SD0_MAPPING == SD0MAP_G4)
    usb_chk_sta = 1;
    FUNCMCON0 = 0x0f;                       //关SD0 Mapping
    if ((func_cb.sta == FUNC_MUSIC) && dev_is_online(DEV_USBPC)) {  //DM pull up 200k will effect sdcard reading while PC insert
        USBCON1 |= BIT(17);
    } else {
        USBCON1 |= BIT(17) | BIT(18);                               //DP pull down 15k enable to detect udisk,DM pull up 200k enable to detect PC
    }
    SD_DAT_DIS_UP();
    delay_us(1);
#endif
#endif

#if FUNC_USBDEV_EN || UDE_HID_BACKSTAGE_EN
    u8 usb_sta = usbchk_connect(USBCHK_OTG);
#else
    u8 usb_sta = usbchk_connect(USBCHK_ONLY_HOST);
#endif

#if SD_USB_MUX_IO_EN  && (SD0_MAPPING == SD0MAP_G5 || SD0_MAPPING == SD0MAP_G4)

    if (sys_cb.cur_dev != DEV_UDISK)
    {
        SD_DAT_RES_UP();
        USBCON1 &= ~ (BIT(17) | BIT(18) | BIT(19));
#if MUSIC_SDCARD1_EN
        FUNCMCON0 = SD1_MAPPING;
#else
        FUNCMCON0 = SD0_MAPPING;
#endif
        delay_us(1);
    }
    usb_chk_sta = 0;

#endif

    if (usb_sta == USB_UDISK_CONNECTED) {
        if (dev_online_filter(DEV_UDISK)) {
            udisk_insert();
            msg_enqueue(EVT_UDISK_INSERT);
            my_printf(usb_insert_str);
        }
    } else {
        if (dev_offline_filter(DEV_UDISK)) {
            udisk_remove();
            msg_enqueue(EVT_UDISK_REMOVE);
            my_printf(usb_remove_str);
        }
    }
#if FUNC_USBDEV_EN || UDE_HID_BACKSTAGE_EN
    if (usb_sta == USB_PC_CONNECTED) {
        if (dev_online_filter(DEV_USBPC)) {
            msg_enqueue(EVT_PC_INSERT);
            my_printf(pc_insert_str);
        }
    } else {
        if (dev_offline_filter(DEV_USBPC)) {
            msg_enqueue(EVT_PC_REMOVE);
            pc_remove();
            my_printf(pc_remove_str);
        }
    }
#endif
}
#endif // USB_SUPPORT_EN

#if MICL_MUX_DETECT_LINEIN
AT(.com_text.detect)
bool linein_micl_is_online(void)
{
    if (!sys_cb.micl_en) {
        return (!(GPIOF & BIT(2)));
    } else {
        return (micl2gnd_flag);
    }
}
#endif // MICL_MUX_DETECT_LINEIN

#if LINEIN_DETECT_EN
AT(.com_text.detect)
void linein_detect(void)
{
    if (IS_DET_LINEIN_BUSY()) {
        return;
    }
    if (LINEIN_IS_ONLINE()) {
        if (dev_online_filter(DEV_LINEIN)) {
            msg_enqueue(EVT_LINEIN_INSERT);
            my_printf("linein insert\n");
        }
    } else {
        if (dev_offline_filter(DEV_LINEIN)) {
            msg_enqueue(EVT_LINEIN_REMOVE);
            my_printf("linein remove\n");
        }
    }
}
#endif // LINEIN_DETECT_EN

#if EARPHONE_DETECT_EN
AT(.com_text.detect)
void earphone_detect(void)
{
    if (IS_DET_EAR_BUSY()) {
        return;
    }
    if (EARPHONE_IS_ONLINE()) {
        if (dev_online_filter(DEV_EARPHONE)) {
            my_printf("earphone insert\n");
        }
    } else {
        if (dev_offline_filter(DEV_EARPHONE)) {
            my_printf("earphone remove\n");
        }
    }

    if (dev_is_online(DEV_EARPHONE)) {
        LOUDSPEAKER_MUTE();
    } else {
        if (!sys_cb.loudspeaker_mute) {
            LOUDSPEAKER_UNMUTE();
        }
    }
}
#endif // EARPHONE_DETECT_EN

#if MIC_DETECT_EN
AT(.com_text.detect)
void mic_detect(void)
{
    if (IS_DET_MIC_BUSY()) {
        return;
    }
    if (MIC_IS_ONLINE()) {
        if (dev_online_filter(DEV_MIC)) {
            msg_enqueue(EVT_MIC_INSERT);
            my_printf("mic insert\n");
        }
    } else {
        if (dev_offline_filter(DEV_MIC)) {
            msg_enqueue(EVT_MIC_REMOVE);
            my_printf("mic remove\n");
        }
    }
}
#endif // EARPHONE_DETECT_EN

#if BT_PWRKEY_5S_DISCOVER_EN
AT(.com_text.detect)
void pwrkey_5s_on_check(void)
{
#if 0
    if (!xcfg_cb.bt_pwrkey_nsec_discover) {
        return;
    }
#endif
    if (sys_cb.pwrkey_5s_check) {
        if (0 == (RTCCON & BIT(19))) {
            #if 0
            if (tick_check_expire(sys_cb.ms_ticks, 1000 * xcfg_cb.bt_pwrkey_nsec_discover)) 
            #endif
            {
                sys_cb.pwrkey_5s_flag = 1;
                sys_cb.pwrkey_5s_check = 0;
            }
        } else {
            sys_cb.pwrkey_5s_check = 0;
        }
    }
}
#endif // BT_PWRKEY_5S_DISCOVER_EN

//timer tick interrupt(1ms)
AT(.com_text.timer)
void usr_tmr1ms_isr(void)
{
#if (GUI_SELECT & DISPLAY_LEDSEG)
    gui_scan();                     //7P屏按COM扫描时，1ms间隔
#endif

#if FUNC_FMRX_EN
    fmrx_tmr1ms_isr();
#endif // FUNC_FMRX_EN

#if LED_DISP_EN
    port_2led_scan();
#endif // LED_DISP_EN

    plugin_tmr1ms_isr();

#if CHARGE_DC_RESET_DELAY
    if (sys_cb.dc_rst_flag == 1 && CHARGE_DC_IN()) {
#if BT_TWS_AUTO_SWITCH
        msg_enqueue(EVT_BT_TWS_SWITCH);
#endif // BT_TWS_AUTO_SWITCH
        sys_cb.dc_rst_flag = 2;
        sys_cb.dc_rst_tout = 0;
    } else if (sys_cb.dc_rst_flag >= 2) {
        if (++sys_cb.dc_rst_tout > 3500) {
            WDT_RST();
        }
    }
#endif
}

//timer tick interrupt(5ms)
AT(.com_text.timer)
void usr_tmr5ms_isr(void)
{
    tmr5ms_cnt++;
    //5ms timer process
    dac_fade_process();

#if EXFLASH_MUSIC_WARNING
    if (wav_output_en) {
        wav_output_tick_start();
    }
#endif

    bsp_key_scan();

    plugin_tmr5ms_isr();
#if HDMI_DETECT_EN && FUNC_HDMI_EN
    hdmi_detect();
#endif
#if MUSIC_SDCARD_EN
    sd_detect();
#endif // MUSIC_SDCARD_EN

#if MUSIC_SDCARD1_EN
    sd1_detect();
#endif // MUSIC_SDCARD1_EN

#if USB_SUPPORT_EN
    usb_detect();
#endif // USB_SUPPORT_EN

#if LINEIN_DETECT_EN
    linein_detect();
#endif // LINEIN_DETECT_EN

#if EARPHONE_DETECT_EN
    earphone_detect();
#endif // EARPHONE_DETECT_EN

#if MIC_DETECT_EN
    mic_detect();
#endif // MIC_DETECT_EN

    //50ms timer process
    if ((tmr5ms_cnt % 10) == 0) {
        ticks_50ms++;
        led_scan();
#if BT_PWRKEY_5S_DISCOVER_EN
        pwrkey_5s_on_check();
#endif // BT_PWRKEY_5S_DISCOVER_EN
    }

    //100ms timer process
    if ((tmr5ms_cnt % 20) == 0) {
        lowpwr_tout_ticks();
#if UDE_HID_EN
        ude_tmr_isr();
#endif // UDE_HID_EN
        gui_box_isr();                  //显示控件计数处理
#if CHARGE_EN
        if (xcfg_cb.charge_en) {
            charge_detect();
        }
#endif // CHARGE_EN

        if (sys_cb.lpwr_cnt > 0) {
            sys_cb.lpwr_cnt++;
        }

        if (sys_cb.key2unmute_cnt) {
            sys_cb.key2unmute_cnt--;
            if (!sys_cb.key2unmute_cnt) {
                msg_enqueue(EVT_KEY_2_UNMUTE);
            }
        }

    }

    //500ms timer process
    if ((tmr5ms_cnt % 100) == 0) {
        sys_cb.cm_times++;
#if FUNC_CLOCK_EN
        msg_enqueue(MSG_SYS_500MS);
#endif // FUNC_CLOCK_EN
    }

    //1s timer process
    if ((tmr5ms_cnt % 200) == 0) {
        msg_enqueue(MSG_SYS_1S);
        tmr5ms_cnt = 0;
        sys_cb.lpwr_warning_cnt++;
    }
}

AT(.com_text.timer)
void timer1_isr(void)
{
    if (TMR1CON & BIT(9)) {
        TMR1CPND = BIT(9);              //Clear Pending
        TMR1CON = 0;
#if (GUI_SELECT == GUI_LEDSEG_7P7S)
        ledseg_7p7s_clr();              //close display
#endif
    }
}

AT(.com_text.bsp.sys)
void bsp_loudspeaker_mute(void)
{
    LOUDSPEAKER_MUTE();
    sys_cb.loudspeaker_mute = 1;
}

AT(.com_text.bsp.sys)
void bsp_loudspeaker_unmute(void)
{
    sys_cb.loudspeaker_mute = 0;
#if EARPHONE_DETECT_EN
    if (!dev_is_online(DEV_EARPHONE))
#endif // EARPHONE_DETECT_EN
    {
        LOUDSPEAKER_UNMUTE();
    }
}

AT(.text.bsp.sys)
void bsp_sys_mute(void)
{
    if (!sys_cb.mute) {
        sys_cb.mute = 1;
#if (GUI_SELECT != GUI_NO)
        gui_box_flicker_set(5, 0xff, 0xff);
#endif
        dac_fade_out();
        bsp_loudspeaker_mute();
#if DAC_DNR_EN
        dac_dnr_set_sta(0);
#endif
    }
}

AT(.text.bsp.sys)
void bsp_sys_unmute(void)
{
    if (sys_cb.mute) {
        sys_cb.mute = 0;
        bsp_loudspeaker_unmute();
        dac_fade_in();
#if DAC_DNR_EN
        dac_dnr_set_sta(sys_cb.dnr_sta);
#endif
    }
}

AT(.text.bsp.sys)
void bsp_clr_mute_sta(void)
{
    if (sys_cb.mute) {
        sys_cb.mute = 0;
#if DAC_DNR_EN
        dac_dnr_set_sta(sys_cb.dnr_sta);
#endif
    }
}

AT(.com_text.bsp.sys)
bool bsp_get_mute_sta(void)
{
    return sys_cb.mute;
}


//PWRKEY 引脚通过开关80K上拉/10K下拉, 来实现输出高低电平, 注意高电平为1.8V
//AT(.com_text.bsp.sys)
//void powerkey_out_set(bool high)  //里面调用到的函数都在公共区.
//{
//    u8 rtccon1 = irtc_sfr_read(RTCCON1_CMD);
//    if (high) {
//        rtccon1 &= ~(BIT(1)| BIT(2) | BIT(3)) ;  //bit1: 10K PD DISABLE  //bit2,bit3 = 00, PU IS 80K
//        rtccon1 |= BIT(4) ;                      //PU ENABLE,
//    } else {
//        rtccon1 &= ~BIT(4) ;      //PU DISABLE
//        rtccon1 |= BIT(1) ;       //10K PD ENABLE
//      u8 rtccon7 = irtc_sfr_read(RTCCON7_CMD);
//        if (!(rtccon7 & BIT(4))){ //PD enable protect bit.  PD enable must "rtccon7.4 = 1 and rtccon1.1 = 1"
//            rtccon7 |= BIT(4);
//            irtc_sfr_write(RTCCON7_CMD, rtccon7);
//        }
//    }
//    irtc_sfr_write(RTCCON1_CMD, rtccon1);
//}

AT(.text.bsp.sys.init)
static void rtc_32k_configure(void)
{
    u8 temp = irtc_sfr_read(RTCCON0_CMD);

#if EXT_32K_EN
    temp |= BIT(1);                             //enable 32.768k osc
    temp &= ~BIT(6);                            //disable ext_32k
    irtc_sfr_write(RTCCON0_CMD, temp);
    temp = irtc_sfr_read(RTCCON2_CMD);
    irtc_sfr_write(RTCCON2_CMD, temp & ~BIT(7));//select 32.768k osc
#else
    temp &= ~BIT(1);                            //disable 32k osc
    temp |= BIT(6);                             //enable ext_32k
    irtc_sfr_write(RTCCON0_CMD, temp);
    temp = irtc_sfr_read(RTCCON2_CMD);
    irtc_sfr_write(RTCCON2_CMD, temp | BIT(7)); //select ext_32k
#endif
}

AT(.text.bsp.sys.init)
bool rtc_init(void)
{
    u8 temp;
    rtc_32k_configure();
    sys_cb.rtc_first_pwron = 0;
    temp = irtc_sfr_read(RTCCON0_CMD);
    if (temp & BIT(7)) {
        temp &= ~BIT(7);                            //clear first poweron flag
        irtc_sfr_write(RTCCON0_CMD, temp);
        sys_cb.rtc_first_pwron = 1;
#if FUNC_CLOCK_EN
        rtc_clock_init();
#endif // FUNC_CLOCK_EN
        my_printf("rtc 1st pwrup\n");
        return false;
    }

    return true;
}


#if 0//DEBUG_SPI_LOAD
extern void set_dumpx(void (*func1)(uint info1, ...), void (*func2)(uint info1, ...));
AT(.com_text *)
const char strload[] = "->";

AT(.com_text.debug)
void debug_spi_load(uint info_num, ...)
{
    va_list param;
    va_start(param, info_num);
    if (1 == info_num) {
        printk(strload);
    }
}

void debug_set(void)
{
    set_dumpx(NULL, debug_spi_load);
}
#endif




//UART0打印信息输出GPIO选择，UART0默认G1(PA7)
void uart0_mapping_sel(void)
{
    //等待uart0发送完成
    if (UART0CON & BIT(0)) {
        while (!(UART0CON & BIT(8)));
    }

    GPIOADE  &= ~BIT(7);
    GPIOAFEN &= ~BIT(7);
    GPIOAPU  &= ~BIT(7);
    GPIOBFEN &= ~(BIT(2) | BIT(3));
    GPIOBPU  &= ~(BIT(2) | BIT(3));
    FUNCMCON0 = (0xf << 12) | (0xf << 8);           //clear uart0 mapping

#if (UART0_PRINTF_SEL == PRINTF_PA7)
    GPIOADE  |= BIT(7);
    GPIOAPU  |= BIT(7);
    GPIOADIR |= BIT(7);
    GPIOAFEN |= BIT(7);
    GPIOADRV |= BIT(7);
    FUNCMCON0 = URX0MAP_TX | UTX0MAP_PA7;           //RX0 Map To TX0, TX0 Map to G1
#elif (UART0_PRINTF_SEL == PRINTF_PB2)
    GPIOBDE  |= BIT(2);
    GPIOBPU  |= BIT(2);
    GPIOBDIR |= BIT(2);
    GPIOBFEN |= BIT(2);
    FUNCMCON0 = URX0MAP_TX | UTX0MAP_PB2;           //RX0 Map To TX0, TX0 Map to G2
#elif (UART0_PRINTF_SEL == PRINTF_PB3)
    GPIOBDE  |= BIT(3);
    GPIOBPU  |= BIT(3);
    GPIOBDIR |= BIT(3);
    GPIOBFEN |= BIT(3);
    FUNCMCON0 = URX0MAP_TX | UTX0MAP_PB3;           //RX0 Map To TX0, TX0 Map to G3
#elif (UART0_PRINTF_SEL == PRINTF_PE7)
    GPIOEDE  |= BIT(7);
    GPIOEPU  |= BIT(7);
    GPIOEDIR |= BIT(7);
    GPIOEFEN |= BIT(7);
    FUNCMCON0 = URX0MAP_TX | UTX0MAP_PE7;           //RX0 Map To TX0, TX0 Map to G4
#elif (UART0_PRINTF_SEL == PRINTF_PA1)
    GPIOADE  |= BIT(1);
    GPIOAPU  |= BIT(1);
    GPIOADIR |= BIT(1);
    GPIOAFEN |= BIT(1);
    FUNCMCON0 = URX0MAP_TX | UTX0MAP_PA1;           //RX0 Map To TX0, TX0 Map to G5
#elif (UART0_PRINTF_SEL == PRINTF_PE0)
    GPIOEDE  |= BIT(0);
    GPIOEPU  |= BIT(0);
    GPIOEDIR |= BIT(0);
    GPIOEFEN |= BIT(0);
    FUNCMCON0 = URX0MAP_TX | UTX0MAP_PE0;           //RX0 Map To TX0, TX0 Map to G6
#elif (UART0_PRINTF_SEL == PRINTF_PF2)
    GPIOFDE  |= BIT(0);
    GPIOFPU  |= BIT(0);
    GPIOFDIR |= BIT(0);
    GPIOFFEN |= BIT(0);
    FUNCMCON0 = URX0MAP_TX | UTX0MAP_PF2;           //RX0 Map To TX0, TX0 Map to G7
#endif

#if I2C_PRINT_EN
    my_printf_init(iic_putchar);
#endif
}

const u8 maxvol_tbl[4] = {16, 30, 32, 50};

//开user timer前初始化的内容
AT(.text.bsp.sys.init)
static void bsp_var_init(void)
{
    memset(&sys_cb, 0, sizeof(sys_cb));
    sys_cb.loudspeaker_mute = 1;
    sys_cb.ms_ticks = tick_get();
    sys_cb.pwrkey_5s_check = 1;
    xcfg_cb.vol_max = maxvol_tbl[xcfg_cb.vol_max];
    sys_cb.hfp2sys_mul = (xcfg_cb.vol_max + 2) / 16;
    if (SYS_INIT_VOLUME > xcfg_cb.vol_max) {
        SYS_INIT_VOLUME = xcfg_cb.vol_max;
    }
    if (WARNING_VOLUME > xcfg_cb.vol_max) {
        WARNING_VOLUME = xcfg_cb.vol_max;
    }

    sys_cb.sleep_time = -1L;
    sys_cb.pwroff_time = -1L;
    if (xcfg_cb.sys_sleep_time != 0) {
        sys_cb.sleep_time = (u32)xcfg_cb.sys_sleep_time * 10;   //100ms为单位
    }
    if (xcfg_cb.sys_off_time != 0) {
        sys_cb.pwroff_time = (u32)xcfg_cb.sys_off_time * 10;    //100ms为单位
    }

    sys_cb.sleep_delay = -1L;
    sys_cb.pwroff_delay = -1L;
    sys_cb.sleep_en = 0;
    sys_cb.lpwr_warning_times = LPWR_WARING_TIMES;
    key_var_init();
    plugin_var_init();

    msg_queue_init();

    dev_init(((u8)is_sd_support()) | ((u8)is_usb_support() * 0x02));
#if SD_SOFT_DETECT_EN
    if (SD_IS_SOFT_DETECT()) {
        dev_delay_times(DEV_SDCARD, 3);
    }
#endif

#if SD_USB_MUX_IO_EN && (SD0_MAPPING == SD0MAP_G5||SD0_MAPPING == SD0MAP_G4)
    sd_other_param_set(0x0F);
#endif

    // sd_other_param_set(0x1); //BIT(0) = 1 每次读都会sd_stop.   //BIT(1) = 1 每次写都会sd_stop.使用SD——data复用检测其他设备时需要stop一下
#if MUSIC_SDCARD_EN
    if ((xcfg_cb.sddet_iosel == IO_MUX_SDCLK) || (xcfg_cb.sddet_iosel == IO_MUX_SDCMD)) {
        dev_delay_offline_times(DEV_SDCARD, 3); //复用时, 加快拔出检测. 这里拔出检测为3次.
    }
#endif

#if MUSIC_SDCARD1_EN
    if ((xcfg_cb.sd1det_iosel == IO_MUX_SDCLK) || (xcfg_cb.sd1det_iosel == IO_MUX_SDCMD)) {
        dev_delay_offline_times(DEV_SDCARD1, 3); //复用时, 加快拔出检测. 这里拔出检测为3次.
    }
#endif

#if FUNC_AUX_EN
    aux_var_init();
#endif
    sdadc_var_init();
#if FUNC_REC_EN
    record_var_init();
#endif
    fs_var_init();
    music_stream_var_init();
#if SYS_KARAOK_EN
    karaok_var_init();
#endif
    fmrx_var_init();

    dac_cb_init((DAC_CH_SEL & 0x0f) | (DAC_LOWPWR_EN * 0x10) | (DAC_FAST_SETUP_EN * 0x20) | ((u8)DAC_LDOH_SEL << 6) | ((u16)DAC_VCM_CAPLESS_EN * 0x200));
}

AT(.text.bsp.sys.init)
static void bsp_io_init(void)
{
    GPIOADE = BIT(7); //UART
    GPIOBDE = 0;
    GPIOEDE = 0;
    GPIOFDE = 0;
    GPIOGDE = 0x3F; //MCP FLASH

    uart0_mapping_sel();        //调试UART IO选择或关闭

#if LINEIN_DETECT_EN
    LINEIN_DETECT_INIT();
#endif // LINEIN_DETECT_EN

#if MUSIC_SDCARD_EN
    SD_DETECT_INIT();
#endif // MUSIC_SDCARD_EN

#if MUSIC_SDCARD1_EN
    SD1_DETECT_INIT();
#endif // MUSIC_SDCARD1_EN

#if EARPHONE_DETECT_EN
    EARPHONE_DETECT_INIT();
#endif // EARPHONE_DETECT_EN

    LOUDSPEAKER_MUTE_INIT();
    AMPLIFIER_SEL_INIT();

#if MIC_DETECT_EN
    MIC_DETECT_INIT();
#endif
}

void bsp_get_xosc_xcfg(u8 *osci_cap, u8 *osco_cap, u8 *both_cap)
{
    *osci_cap = xcfg_cb.osci_cap;
    *osco_cap = xcfg_cb.osco_cap;
    *both_cap = xcfg_cb.osc_both_cap;
}

#if BT_TWS_EN
AT(.text.bsp.sys.init)
bool tws_pwrkey_820k_check(void)  //TWS PWRKEY 820K接地为左声道
{
    u8 i, cnt;
    u16 adc_val;
    bool res = false;
    while (1) {
        if (RTCCON & BIT(19)) {  //等待PWRKEY 松开.
            break;
        }
        delay_ms(5);
        WDT_CLR();
    }

    i = 10;
    cnt = 0;
    while (i--) {
        WDT_CLR();
        delay_ms(7);
        adc_val = ((u32)adc_cb.wko_val << 8) / adc_cb.vrtc_val;
        //pu= 90k  pd=820.000K adc=230    //max 255   //min 150k--159
        if ((adc_val > 200) && (adc_val < 245)) {
            cnt++;
        }
        //my_printf("[%d_%d_%d]\n",adc_val,adc_cb.vrtc_val,adc_cb.wko_val);
        if (cnt >= 5) {
            res = true;
            break;
        }
    }
    //my_printf("res = %d,i = %d, cnt = %d\n",res,i,cnt);
    return res;
}

AT(.text.bsp.sys.init)
bool tws_gpiox_2_gnd_check(void)  //TWS 特定GPIO 接地为左声道
{
    u8 i, cnt;
    bool res = false;
    gpio_t tws_sel_left_gpio;
    gpio_t *g = &tws_sel_left_gpio;
    u8 io_num = xcfg_cb.tws_sel_left_gpio_sel;
    //my_printf("io_num = %d\n",io_num);
    if ((!io_num) || (io_num > IO_PG0)) {
        return false;
    }
    bsp_gpio_cfg_init(&tws_sel_left_gpio, xcfg_cb.tws_sel_left_gpio_sel);
    //io sta backup
    u32 bk_de = g->sfr[GPIOxDE];
    u32 bk_pu = g->sfr[GPIOxPU];
    u32 bk_dir = g->sfr[GPIOxDIR];
    //数字IO, 开上拉.
    g->sfr[GPIOxDE] |= BIT(g->num);
    g->sfr[GPIOxPU] |= BIT(g->num);
    g->sfr[GPIOxDIR] |= BIT(g->num);

    i = 6;
    cnt = 0;
    while (i--) {
        delay_ms(5);
        if (!(g->sfr[GPIOx] & BIT(g->num)) ) {   //to GND
            //my_printf("GND\n");
            cnt++;
        } else {
            //my_printf("!!!PU\n");
        }
        if (cnt >= 3) {
            res = true;
            break;
        }
    }
    //检测完成, 恢复以前IO状态
    g->sfr[GPIOxDE] =  bk_de;
    g->sfr[GPIOxPU] =  bk_pu;
    g->sfr[GPIOxDIR] = bk_dir;
    //my_printf("res = %d,i = %d, cnt = %d\n",res,i,cnt);
    return res;
}

AT(.text.bsp.sys.init)
void tws_lr_xcfg_sel(void)
{
    static bool checked_flag = false;  //只检测一次.
    if ((!xcfg_cb.bt_tws_en) || (checked_flag)) {
        return;
    }
    sys_cb.tws_left_channel = 0;
    //my_printf("xcfg_cb.bt_tws_lr_mode = %d\n",xcfg_cb.bt_tws_lr_mode);
    if (2 == xcfg_cb.bt_tws_lr_mode) { //有PWRKEY 820K接地则为左声道
        checked_flag = true;
        if (tws_pwrkey_820k_check()) {
            sys_cb.tws_left_channel = 1;
        }
    } else if (3 == xcfg_cb.bt_tws_lr_mode) { //有GPIOx接地则为左声道
        checked_flag = true;
        if (tws_gpiox_2_gnd_check()) {
            sys_cb.tws_left_channel = 1;
        }
    }
}
#endif


AT(.text.bsp.sys.init)
void bsp_update_init(void)
{
    /// config
    if (!xcfg_init(&xcfg_cb, sizeof(xcfg_cb))) {           //获取配置参数
        my_printf("xcfg init error\n");
    }
    my_printf("LOWPOWER_WARNING_EN: %d\n", xcfg_cb.lowpower_warning_en);

    // io init
    bsp_io_init();

    // var init
    bsp_var_init();
    sys_cb.lang_id = 0;

    // peripheral init
    rtc_init();
    param_init(sys_cb.rtc_first_pwron);

    //26M晶振配置captune
    xosc_init();

    plugin_init();
    sys_set_tmr_enable(1, 1);

    dac_init();
    mp3_res_play(RES_BUF_UPDATE_DONE_MP3, RES_LEN_UPDATE_DONE_MP3);
}

/*
AT(.com_rodata.isr)
const char str_t4[] = "T4 ";

AT(.com_text.isr)
void timer4_isr(void)
{
    if (TMR4CON & BIT(16)) {
        TMR4CPND = BIT(16);              //Clear Pending
    }
    printk(str_t4);
}
*/
/* uart1 2 my_printf define */
#if 1//(UART_UPD_PORT_SEL&UART_TX0_G3_PB3==UART_TX0_G3_PB3)&&UART_S_UPDATE
#define UART_BAUD 1500000
#define UART_BAUD_VAL (((26000000 + (UART_BAUD / 2)) / UART_BAUD) - 1)
void uart1_putchar(u8 ch);
AT(.comm)
void uart1_putchar_2(char ch)
{
    // WDT_CLR();
    while (!(UART1CON & BIT(8)));
    UART1DATA = ch;
}
void my_printf_init(void (*putchar)(char));
#endif
#if 0
void uart1_init(void)
{
    FUNCMCON0 |= (0x0f << 24);
    GPIOADE |= BIT(4);                                         //Port Function EN
    UART1BAUD = (UART_BAUD_VAL << 16) | UART_BAUD_VAL;
    UART1CON = BIT(4) | BIT(0);                                 //2 Stop bits, Uart EN
    UART1CPND = BIT(17) | BIT(16);                              //CLR Pending
    FUNCMCON0 |= (2 << 24);                                      //TX1 Map to G1(PA4)
    GPIOADIR &= ~BIT(4);
    GPIOAFEN |= BIT(4);

}
#endif


//u8 get_xosc_strength_cfg(void)   //0~7 (3bit valid),  default is 1  //xosc_init中会回调此函数
//{
//    return 1;
//}


AT(.com_text.overclk)
bool spiflash_speed_up_en(void)   //SPI FLASH是否使用数据2线并提高SPCLK, 默认不提速度
{
    return SPIFLASH_SPEED_UP_EN;
}

#if SYS_OVERCLK_SEL
AT(.com_text.overclk)
u8 sys120m_overclk_sel(void)
{
    return SYS_OVERCLK_SEL;
}

AT(.text.voltage.trim)
u8 vddcore_default_set(void)
{
    return  0x0A;        //0x0C (vddcore max)
}

void update_uart0baud_in_sysclk_set(u32 uart_baud) {}  //打印的UART0时钟源选则x26m,禁止set_sys_clk中设置UART0BAUD
#endif

void print_comm_info(void)
{
    extern u32 __comm_start,  __comm_end, __bcomm_start, __bcomm_end;
    u32 comm_size = (u32)&__comm_end -   (u32)&__comm_start;
    u32 bcomm_size = (u32)&__bcomm_end - (u32)&__bcomm_start;
    bcomm_size = bcomm_size;
    comm_size = comm_size;
    my_printf("comm(34K)[0x%X,0x%X](used_%d),remain = %d\n", (u32)&__comm_start, (u32)&__comm_end, comm_size, 34 * 1024 - comm_size);
    my_printf("bcomm(8K)[0x%X,0x%X](used_%d),remain = %d\n", (u32)&__bcomm_start, (u32)&__bcomm_end, bcomm_size, 8 * 1024 - bcomm_size);
}

AT(.text.bsp.sys.init)
void bsp_sys_init(void)
{
    /// config
    if (!xcfg_init(&xcfg_cb, sizeof(xcfg_cb))) {           //获取配置参数
        my_printf("xcfg init error\n");
    }
    print_comm_info();
    // io init
    bsp_io_init();

    // var init
    bsp_var_init();

    // clock init
    if (SYS_CLK_SEL < SYS_48M && xcfg_cb.bt_tws_en && BT_TWS_EN) {   //TWS 系统时钟至少48M
        set_sys_clk(SYS_48M);
    } else {
        set_sys_clk(SYS_CLK_SEL);  //SYS_CLK_SEL
    }

    // power init
    pmu_init(BUCK_MODE_EN * xcfg_cb.buck_mode_en, bt_get_hvbko(xcfg_cb.bt_rf_param));

    // peripheral init
    rtc_init();
    param_init(sys_cb.rtc_first_pwron);

    //26M晶振captune配置
    xosc_init();
    plugin_init();
#if FUNC_HDMI_EN && HDMI_DETECT_EN
    hdmi_detect_io_init();
    dev_delay_online_times(DEV_HDMI, 3);
#endif

#if IRRX_SW_EN
    irrx_sw_init();
#endif // IRRX_SW_EN

#if IRRX_HW_EN
    irrx_hw_init();
#endif // IRRX_HW_EN

    if (POWKEY_10S_RESET) {
        irtc_sfr_write(RTCCON9_CMD, 0);     //10S Reset
    }

#if CHARGE_EN
    if (xcfg_cb.charge_en) {
        charge_init();
    }
#endif // CHARGE_EN

    led_init();
    key_init();

    gui_init();
#if PWM_RGB_EN
    pwm_init();
#endif // PWM_RGB_EN
    /// enable user timer for display & dac
    sys_set_tmr_enable(1, 1);
#if (SPI1_AUDIO_EN && !SPI1_RCV_EN)
    dac_to_spi_enable(1);       //音频发送使能
    spi1_tx_init(2500000);      //SPI发送端设置三线模式波特率测试2M~3.3M可用 两线模式
#else
    dac_to_spi_enable(0);       //关闭音频发送
#endif


    led_power_up();
    gui_display(DISP_POWERON);
#if FUNC_FMRX_EN
    bsp_fmrx_get_type();
#endif // FUNC_FMRX_EN

    dac_init();

#if I2S_EN && (!I2S_MODE_SEL) && (!I2S_DMA_EN)
    my_printf("i2s src master tx init\n");
    bsp_i2s_init();
#endif

#if SPI1_AUDIO_EN
    spi1_play();
#endif

#if FMTX_EN
    bsp_fmtx_init();
#endif
    bt_init();
    //sys_cb.vol = VOL_MAX;
    bsp_change_volume(sys_cb.vol);
#if EX_SPIFLASH_SUPPORT
    exspiflash_init();
#endif

#if WARNING_POWER_ON
    mp3_res_play(RES_BUF_POWERON_MP3, RES_LEN_POWERON_MP3);
#endif // WARNING_POWER_ON

    if (PWRON_ENTER_BTMODE_EN) {
        func_cb.sta = FUNC_BT;
        if (dev_is_online(DEV_UDISK)) {
            sys_cb.cur_dev = DEV_UDISK;
        } else {
            sys_cb.cur_dev = DEV_SDCARD;
        }
    } else {
#if SD_SOFT_DETECT_EN
        sd_soft_detect_poweron_check();
#endif
        if (dev_is_online(DEV_USBPC)) {
#if UDE_HID_BACKSTAGE_EN
            usb_device_enter(0x04);
#else
            func_cb.sta = FUNC_USBDEV;
#endif
        } else if (dev_is_online(DEV_SDCARD) || dev_is_online(DEV_UDISK) || dev_is_online(DEV_SDCARD1)) {
            func_cb.sta = FUNC_MUSIC;
            if (dev_is_online(DEV_UDISK)) {
                sys_cb.cur_dev = DEV_UDISK;
            } else if (dev_is_online(DEV_SDCARD1)) {
                sys_cb.cur_dev = DEV_SDCARD1;
            } else {
                sys_cb.cur_dev = DEV_SDCARD;
            }
        } else {
#if FUNC_AUX_EN
            if (dev_is_online(DEV_LINEIN)) {
                func_cb.sta = FUNC_AUX;
            } else
#endif

#if FUNC_HDMI_EN
                if (dev_is_online(DEV_HDMI)) {
                    func_cb.sta = FUNC_HDMI;
                    bsp_hdmi_cec_init();
                    cec_rx_start();    //提前开始接收，及时回电视ack信息
                } else
#endif
                {
                    func_cb.sta = FUNC_BT;
                }
        }
    }

#if SYS_MODE_BREAKPOINT_EN
    u8 sta = param_sys_mode_read();
    if (sta != 0 && sta != 0xff) {
        func_cb.sta = sta & 0xf;
        if (func_cb.sta == FUNC_MUSIC) {
            sys_cb.cur_dev = sta >> 4;
        }
    }
#endif // SYS_MODE_BREAKPOINT_EN

#if LINEIN_2_PWRDOWN_EN
    if (dev_is_online(DEV_LINEIN)) {
        sys_cb.pwrdwn_tone_en = LINEIN_2_PWRDOWN_TONE_EN;
        func_cb.sta = FUNC_PWROFF;
    }
#endif // LINEIN_2_PWRDOWN_EN

#if BT_BACKSTAGE_EN
    bsp_bt_init();
#endif

#if EQ_DBG_IN_UART || EQ_DBG_IN_SPP
    eq_dbg_init();
#endif // EQ_DBG_IN_UART

#if PLUGIN_SYS_INIT_FINISH_CALLBACK
    plugin_sys_init_finish_callback(); //初始化完成, 各方案可能还有些不同参数需要初始化,预留接口到各方案
#endif

#if SYS_KARAOK_EN && !AUX_MIC_TWO_CHANNLE
    bsp_karaok_init(AUDIO_PATH_KARAOK, func_cb.sta);
#endif

#if CHARACTER_ENC
    spiflash1_init();
#endif // CHARACTER_ENC

#if BT_SCO_MAV_EN
    sco_magic_config();
#endif // BT_SCO_MAV_EN

#if IODM_TEST_MODE
    // iodm_test_mode_uart1_init();
    iodm_test_mode_uart0_init();
#endif // IODM_TEST_MODE

    uart1_debug_init();

    my_printf("[ModuleLog] Welcome to AB5301\n");
    //iodm_log_printf("[ModuleLog] Welcome to AB5301");


#if UART_M_UPDATE||UART_S_UPDATE
#if 0//(UART_UPD_PORT_SEL&UART_TX0_G3_PB3==UART_TX0_G3_PB3)&&UART_S_UPDATE
    CLKGAT0 |= BIT(21);
    uart1_init();
    my_printf_init(uart1_putchar_2);                              //使用PA4进行Debug
#endif // UART_UPD_PORT_SEL
    uart_upd_init(UART_UPD_PORT_SEL, UART_UPD_BAUD, SYS_CLK_SEL);
#if UART_S_UPDATE
    uart_upd_isr_init();
#endif // UART_S_UPDATE
#endif

#if UDISK_SECTOR_4K_EN
    disk_sector_4k_init(win_sector);
#endif // UDISK_SECTOR_4K_EN

#if SYS_KARAOK_EN && !UDE_MIC_KARAOK_EN
    bsp_karaok_init(AUDIO_PATH_KARAOK, func_cb.sta);
#endif

#if UDE_STOAGE_FLASH_EN
    void spiflash1_init_usb(void);
    spiflash1_init_usb();
    if (spiflash1_manu_id_read()) {
        func_cb.sta = FUNC_MUSIC;
    }
#endif // UDE_STOAGE_FLASH_EN
#if BT_2ACL_EN
    sys_cb.bt_disconnect_a2dp_playing = false;
    sys_cb.bt_set_scan_en  = false;
#endif
}


